/**
 * @file   MySocket.java
 * @author Omar Quintana <o.quintana@lpcsite.com> y Enrique Madrid <e.madrid@lpcsite.com>
 * @date   Mon Sep  3 01:01:01 2007
 * 
 * @brief  
 * 
 * 
 */

package sim.framework.io;

import sim.framework.Simlet;
import sim.utils.*;
import java.io.*;
import javax.microedition.io.*;

public class Socket extends Io {

    /**
     * Socket
     */
    private SocketConnection m_Socket = null;
    private DataInputStream  m_DataIS = null;
    private DataOutputStream m_DataOS = null;

    /**
     * Crea una nueva instancia de Socket
     */
    protected Socket(Simlet s) {
	m_Simlet = s;
    }

    protected static Io create(Simlet s, 
			       final String address, 
			       final String port) throws ConnectionNotFoundException, IllegalArgumentException, IOException {
        Socket d;
        d = new Socket(s);
        if (!d.open(address, port)) {
            d = null;
        }
        return d;
    }

    protected synchronized int lock(byte operation, 
				    final String address, 
				    final String port, 
				    Buffer buffer) throws ConnectionNotFoundException, IllegalArgumentException, IOException {
        return lockedFor(operation, address, port, buffer);
    }
    
    protected int lockedFor(byte operation, 
			    final String address, 
			    final String port, 
			    Buffer buffer) throws ConnectionNotFoundException, IllegalArgumentException, IOException {
        int ret;
        ret = Io.ERR;
        switch (operation) {
	case Io.OPER_OPEN:
	    ret = lockedForOpen(address, port);
	    break;
	case Io.OPER_CLOSE:
	    ret = lockedForClose();
	    break;
	case Io.OPER_FLUSH:
	    ret = lockedForFlush();
	    break;
	case Io.OPER_WRITE:
	    ret = lockedForWrite(buffer);
	    break;
	case Io.OPER_READ:
	    ret = lockedForRead(buffer);
	    break;
        }
        return ret;
    }
    
    protected int lockedForOpen(final String address, 
				final String port) throws ConnectionNotFoundException, IllegalArgumentException, IOException {
	int ret = 0;
        try {
	    // Crear un socket
            m_Socket = (SocketConnection)Connector.open("socket://" + address + ":" + port, 
							Connector.READ_WRITE);
	    // Configurar socket
	    m_Socket.setSocketOption(SocketConnection.DELAY, 0);
	    m_Socket.setSocketOption(SocketConnection.KEEPALIVE, 0);
	    // 	    m_Socket.setSocketOption(SocketConnection.LINGER, 0);
	    // 	    m_Socket.setSocketOption(SocketConnection.RCVBUF, 512);
	    // 	    m_Socket.setSocketOption(SocketConnection.SNDBUF, 128);
	    // Flujos de entrada y salida.
            m_DataIS = m_Socket.openDataInputStream();
            m_DataOS = m_Socket.openDataOutputStream();
	} catch (SecurityException se) {
	    m_Simlet.execFail("Socket.lockedForOpen() SecurityException");
        } finally {
	    if (!opened()) {
		ret = Io.ERR;
		lockedForClose();
	    }
	}

        return ret;
    }

    protected boolean opened() {
	return ((m_Socket != null) && (m_DataIS != null) && (m_DataOS != null));
    }
    
    protected int lockedForClose() {
        try {
	    // Fundamental!!!, primer se debe cerrar los Streams.
            if (m_DataIS != null) {
                m_DataIS.close();
                m_DataIS = null;
            }
            if (m_DataOS != null) {
                m_DataOS.close();
                m_DataOS = null;
            }
	    // Ahora si cerramos el socket.
            if (m_Socket != null) {
                m_Socket.close();
                m_Socket = null;
            }
        } catch (IOException ioe) {
	    m_Simlet.execFail("Socket.lockedForClose() IOException");
        }
	return 0;
    }

    protected int lockedForFlush() throws IOException {
	m_DataOS.flush();
	return 0;
    }
    
    protected int lockedForWrite(final Buffer buffer) throws IOException {
	int ret;
	//#ifdef DEBUG_SOCKET
	//# System.out.println("Socket::lockedForWrite() ENTERED");
	//#endif

	//#ifdef DEBUG_SOCKET
	//# String Encoding = "CP1251";
	//# System.out.println("Bytes para enviar: " + buffer.length());
	//# System.out.println("Utilizando decodificación : " + Encoding);
	//# System.out.println("\t---- Begin ----");
	//# System.out.println(new String(buffer.bytes(), buffer.offset(), buffer.length(), Encoding));
	//# System.out.println("\t---- End   ----");
	//#endif

	ret = buffer.length();
	if (ret > 0) {
	    m_DataOS.write(buffer.bytes(), buffer.offset(), ret);
	    m_DataOS.flush();
	}

	//#ifdef DEBUG_SOCKET
	//# System.out.println("Socket::lockedForWrite() EXITING");
	//#endif
	return ret;
    }
    
    protected int lockedForRead(Buffer buffer) throws IOException {
	int ret;
	int b;

	//#ifdef DEBUG_SOCKET
	//# System.out.println("Socket::lockedForRead() ENTERED");
	//#endif

	ret = 0;
	do {
	    b = m_DataIS.read();
	    if (b >= 0) {
		buffer.append((byte)b);
		ret ++;
	    }
	    else if (b == -1) {
		ret = -1;
		break;
	    }
	} while ((m_DataIS.available() > 0));
	
	//#ifdef DEBUG_MODSIMIO
	//# String Encoding = "CP1251";
	//# System.out.println("Bytes recibidos: " + ret);
	//# System.out.println("Utilizando decodificación : " + Encoding);
	//# System.out.println("\t---- Begin ----");
	//# if (ret > 0) {
	//# System.out.println(new String(buffer.bytes(), buffer.length() - ret, ret, Encoding));
	//# }
	//# System.out.println("\t---- End   ----");
	//#endif

	//#ifdef DEBUG_SOCKET
	//# System.out.println("Socket::lockedForRead() EXITING");
	//#endif

        return ret;
    }

    /** 
     * 
     * 
     * @param address 
     * @param port 
     * 
     * @return 
     */    
    protected boolean open(final String address, 
			   String port) throws ConnectionNotFoundException, IllegalArgumentException, IOException {
	return (lock(OPER_OPEN, address, port, null) == 0);
    }

    /** 
     * 
     * 
     * 
     * @return 
     */
    protected boolean close() {
	boolean ret = false;
	// Debemos deshacernos de todos los Exceptions que lock() propaga.
	try {
	    ret = (lock(OPER_CLOSE, null, null, null) == 0);
	}
	catch (ConnectionNotFoundException cnfe) {
	}
	catch (IllegalArgumentException iae) {
	}
	catch (IOException ioe) {
	}
	return ret;
    }

    /** 
     * 
     * 
     * 
     * @return 
     */
    protected boolean flush() throws IOException {
        return (lock(OPER_FLUSH, null, null, null) == 0);
    }
    
    /** 
     * 
     * 
     * @param buffer 
     * 
     * @return 
     */
    protected int write(final Buffer buffer) throws IOException {
        return lock(OPER_WRITE, null, null, buffer);
    }

    /** 
     * Lee bytes del 
     * 
     * @param buffer 
     * 
     * @return 
     */
    protected int read(Buffer buffer) throws IOException {
        return lock(OPER_READ, null, null, buffer);
    }
}
